﻿using MuseLibrary.EncodingHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MuseLibrary.Packets
{
    public class CompressedEegPacket : Packet
    {
        internal CompressedEegPacket()
        {
            _rawData = new List<int>(9);
            _canHaveFlags = false;
            _hasPostInitialisation = true;
        }

        #region Methods

        protected override void DoInitialisation()
        {
            var intermediateValues = _rawData.To10BitIntegers(1, 4);
            _quantisation = new int[4];
            _median = new int[4];

            for (int i = 0; i < 4; i++)
            {
                var median = intermediateValues[i].DropLeadingBits(4);
                _median[i] = median;
                if (median != 0)
                    _quantisation[i] = ParseQuantisation(intermediateValues[i] >> 6);
            }
        }

        private int ParseQuantisation(int value)
        {
            var output = 1;
            if ((value & 1) == 1)
                output *= 2;
            if ((value & 2) == 2)
                output *= 4;
            if ((value & 4) == 4)
                output *= 8;
            if ((value & 8) == 8)
                output *= 16;
            return output;
        }

        public override void PostInitialisation(int[] channelValues)
        {
            int channelIndex = 0;

            var bits = _rawData.Subset(8).ToBitArray();
            var bitIndex = 0;
            var quotientParser = new QuotientParser();
            var remainderParser = new RemainderParser();
            int sampleIndex = 0;

            while (bitIndex < bits.Count())
            {
                var median = _median[channelIndex];
                var quantisation = _quantisation[channelIndex];
                if (median == 0)
                {
                    for (int i = 0; i < 16; i++)
                        _channels[channelIndex][i] = 0;
                    bitIndex += 16 * 3;
                    continue;
                }

                quotientParser.ResetState();
                while (!quotientParser.ReadBit(bits[bitIndex++])) { }
                var quotient = quotientParser.Quotient;

                remainderParser.ResetState(median);
                while (!remainderParser.ReadBit(bits[bitIndex++])) { }
                var remainder = remainderParser.Remainder;

                var sign = (bits[bitIndex]) ? -1 : 1;
                bitIndex++;

                var delta = (quotient * median + remainder) * sign * quantisation;
                var channelValue = channelValues[channelIndex] + delta;
                channelValues[channelIndex] = channelValue;
                _channels[channelIndex][sampleIndex] = channelValue;

                if (sampleIndex == 15)
                {
                    if (channelIndex == 3)
                        break;
                    channelIndex++;
                    sampleIndex = 0;
                }
                else
                {
                    sampleIndex++;
                }
            }
        }

        #endregion

        #region Properties
        
        public Channels Channels
        {
            get { return _channels; }
        }

        public override PacketType Type
        {
            get { return PacketType.CompressedEeg; }
        }

        public override int Length
        {
            get
            {
                if (!_length.HasValue)
                {
                    if (_rawData.Count < 8)
                        return Int32.MaxValue;
                    else
                    {
                        Double bits = (_rawData[6] << 8) + _rawData[7];
                        _length = (int)Math.Ceiling(((bits) / 8.0) + 8.0);
                    }
                }

                return _length.Value;
            }
        }

        #endregion

        #region Fields

        private int? _length = null;
        private Channels _channels = new Channels();

        private int[] _quantisation;
        private int[] _median;

        #endregion
    }
}
